/**
 * Customizeable scrollbar control.
 * 
 * @author Andrey Anisimov <hercules@inbox.ru>
 * @see core.ui.Slider, core.ui.ProgressBar
 */

import core.ui.Slider;
import core.ui.Window;
import core.ui.Event;
import core.ui.ImageButton;

class ScrollBar extends Window {
	
	/**
	 * Creates new scroll bar control.
	 * 
	 * @param parent (Window) [optional] parent window
	 * @param x, y (int) [optional] coordinates
	 * @param width, height (int) [optional] control size.
	 */	
	this.ScrollBar = function(parent, x, y, width, height) {
		super(parent, x, y, width, height);

		this.orienation = ScrollBar.VERTICAL;
		this.step = 0;

		this.backwardButton = new ImageButton(this);
		this.backwardButton.setStyleName('BackwardButton');
		this.backwardButton.setStyle('position', 'absolute');
		this.backwardButton.onmousedown = bind(this, this.onBackwardMouseDown);
				
		this.slider = new Slider(this);
		this.slider.setStyle('position', 'absolute');
		this.slider.addChangeListener(this);
		
		this.forwardButton = new ImageButton(this);
		this.forwardButton.setStyleName('ForwardButton');
		this.forwardButton.setStyle('position', 'absolute');
		this.forwardButton.onmousedown = bind(this, this.onForwardMouseDown);
		
		this.setOrientation(ScrollBar.VERTICAL);
		Event.addListener(this.getElement(), 'resize', bind(this, this.onResize));
		this.scrollHandler = bind(this, this.doScroll);
	}
	
	/**
	 * Sets scrollbar orientation within parent control (ScrollBar.VERTICAL or ScrollBar.HORISONTAL).
	 *
	 * @param orientation (int) new orientation.
	 */
	this.setOrientation = function(o) {
		this.removeStyleName(this.getStyleName() + '-' + (this.isVertical() ? 'vertical' : 'horisontal'));
		this.orientation = o;
		this.addStyleName(this.getStyleName() + '-' + (this.isVertical() ? 'vertical' : 'horisontal'));
		
		this.slider.setVertical(this.isVertical());
		
		this.backwardButton.setStyle('left', '0px');
		this.backwardButton.setStyle('top', '0px');
		this.forwardButton.setStyle('left', this.isVertical() ? '0px' : '');
		this.forwardButton.setStyle('right', this.isVertical() ? '' : '0px');
		this.forwardButton.setStyle('top', this.isVertical() ? '' : '0px');
		this.forwardButton.setStyle('bottom', this.isVertical() ? '0px' : '');
		this.slider.setStyle('left', this.isVertical() ? '0px' : '');
		this.slider.setStyle('top', this.isVertical() ? '' : '0px');
		
		this.updateLayout();
	}
	
	/**
	 * Gets the current scrollbar orientation.
	 *
	 * @return int
	 */
	this.getOrientation = function() {
		return this.orientation;
	}
	
	/**
	 * Gets whether the scrol bar is vertical-oriented.
	 *
	 * @return bool
	 */
	this.isVertical = function() {
		return (ScrollBar.VERTICAL == this.orientation);
	}
	
	/**
	 * Sets scrollbar percentage position.
	 *
	 * @param pos (int) new position in percents.
	 */
	this.setPosition = function(p) {
		p = Math.max(p, 0);
		p = Math.min(p, 100);
		this.slider.setPos(this.isVertical() ? 100 - p : p);
		this.updateTarget(p);
	}
	
	/**
	 * Gets the current scrollbar position in percents.
	 *
	 * @return int
	 */
	this.getPosition = function() {
		var p = this.slider.getPos();
		return this.isVertical() ? 100 - p : p;
	}
	
	/**
	 * Sets target window for scrolling.
	 *
	 * @param target (Window) target window
	 */
	this.setScrollTarget = function(t) {
		this.target = t;
		this.target.setStyle('overflow-' + (this.isVertical() ? 'y' : 'x'), 'hidden');
		this.updateLayout();
	}
	
	/**
	 * Gets the scrolling target window.
	 */
	this.getScrollTarget = function() {
		return this.target;
	}
	
	/**
	 * Returns child button control.
	 *
	 * @return ImageButton
	 */
	this.getBackwardButton = function() {
		return this.backwardButton;
	}
	
	/**
	 * Returns child button control.
	 *
	 * @return ImageButton
	 */
	this.getForwardButton = function() {
		return this.forwardButton;
	}
	
	/**
	 * Returns child slider control.
	 *
	 * @return Slider
	 */
	this.getSliderControl = function() {
		return this.slider;
	}
	
	/**
	 * Update components size and position.
	 */
	this.updateLayout = function() {
		if (this.target instanceof Window) {
			
			if (this.isVertical()) {
				
				this.slider.setStyle('top', this.backwardButton.getHeight() + 'px');
				this.slider.setStyle('height', Math.max(this.getClientHeight() - this.backwardButton.getHeight() - this.forwardButton.getHeight(), 0) + 'px');
				
				// Update indicator height
				var h = (this.target.getElement().scrollHeight > 0) ? this.slider.getClientHeight() * this.target.getClientHeight() / this.target.getElement().scrollHeight : 0;
				this.slider.indicator.setStyle('height', Math.max(h, 8) + 'px');
				
				// Update indicator position
				var p = this.target.getElement().scrollTop / (this.target.getElement().scrollHeight - this.target.getHeight()) * 100;
				
			} else {
				
				this.slider.setStyle('left', this.backwardButton.getWidth() + 'px');
				this.slider.setStyle('width', Math.max(this.getClientWidth() - this.backwardButton.getWidth() - this.forwardButton.getWidth(), 0) + 'px');
				
				// Update indicator width
				var w = (this.target.getElement().scrollWidth > 0) ? this.slider.getClientWidth() * this.target.getClientWidth() / this.target.getElement().scrollWidth : 0;
				this.slider.indicator.setStyle('width', Math.max(w, 8) + 'px');
				
				// Update indicator position
				var p = this.target.getElement().scrollLeft / (this.target.getElement().scrollWidth - this.target.getWidth()) * 100;
			}
			
			// FIXME: assumed zero division result will be NaN (Not-a-Number)
			this.setPosition(isNaN(p) ? 0 : p);
		}
	}
	
	/**
	 * @protected
	 * Updates target scroll position.
	 *
	 * @param pos (int) percentage scrolling position
	 */
	this.updateTarget = function(p) {
		if (this.target instanceof Window) {
			if (this.isVertical()) {
				this.target.getElement().scrollTop = (this.target.getElement().scrollHeight - this.target.getHeight()) * p / 100;
			} else {
				this.target.getElement().scrollLeft = (this.target.getElement().scrollWidth - this.target.getWidth()) * p / 100;
			}
		}
	}
	
	/**
	 * @protected
	 * Performs continuous scrolling when user mouses down scroll button.
	 */
	this.doScroll = function() {
		if (0 != this.step) {
			// TODO: use core.util.Timer
			this.setPosition(this.getPosition() + this.step);
			window.setTimeout(this.scrollHandler, 50);
		}
	}
	
	this.onPositionChanged = function(slider) {
		var p = slider.getPos();
		this.updateTarget(this.isVertical() ? 100 - p : p);
	}
	
	this.onBackwardMouseDown = function() {
		this.attachGlobalEvent('mouseup');
		this.backwardButton.setState(ImageButton.STATE_PRESSED);
		this.setPosition(this.getPosition() + (this.step = -3));
		window.setTimeout(this.scrollHandler, 600);
	}
	
	this.onForwardMouseDown = function() {
		this.attachGlobalEvent('mouseup');
		this.forwardButton.setState(ImageButton.STATE_PRESSED);
		this.setPosition(this.getPosition() + (this.step = 3));
		window.setTimeout(this.scrollHandler, 600);
	}
	
	this.onResize = function() {
		this.updateLayout();
	}
	
	this.onmouseup = function() {
		this.detachGlobalEvent('mouseup');
		this.step = 0;
	}
}

core.ui.ScrollBar.HORISONTAL = 1;
core.ui.ScrollBar.VERTICAL = 2;